package com.platform.module.common.redis;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component("cacheClient")
public class CacheClient {

    private static final Logger log = LoggerFactory.getLogger(CacheClient.class);

    private final RedisTemplate<String, Object> redisTemplate;
    private final StringRedisTemplate stringRedisTemplate;

    private ValueOperations<String, Object> strRedis;

    private ListOperations<String, Object> opsForList;

    private SetOperations<String, Object> opsForSet;

    private ZSetOperations<String, Object> opsForZSet;

    private HashOperations<String, String, Object> opsForHash;

    public CacheClient(RedisTemplate<String, Object> redisTemplate, StringRedisTemplate stringRedisTemplate) {
        this.redisTemplate = redisTemplate;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @PostConstruct
    public void init() {
        this.strRedis = this.redisTemplate.opsForValue();
        this.opsForList = this.redisTemplate.opsForList();
        this.opsForSet = this.redisTemplate.opsForSet();
        this.opsForZSet = this.redisTemplate.opsForZSet();
        this.opsForHash = this.redisTemplate.opsForHash();
    }

    public void leftPushList(String key, List<?> list, Date date) {
        for (Object o : list)
            this.opsForList.leftPush(key, o);
        this.redisTemplate.expireAt(key, date);
    }

    public void rightPushList(String key, List<?> list, Date date) {
        for (Object o : list)
            this.opsForList.rightPush(key, o);
        this.redisTemplate.expireAt(key, date);
    }

    public void addZSet(String key, Set<?> set, double score, Date date) {
        for (Object o : set)
            this.opsForZSet.add(key, o, score);
        this.redisTemplate.expireAt(key, date);
    }

    public List<?> listGet(String key, long start, long end) {
        return this.opsForList.range(key, start, end);
    }

    public Long listSize(String key) {
        return this.opsForList.size(key);
    }

    public List getListByKey(String key, long start, long end) {
        return this.redisTemplate.opsForList().range(key, start, end);
    }

    public Set getZSetByKey(String key, long start, long end) {
        return this.opsForZSet.range(key, start, end);
    }

    public void set(String key, Object obj) {
        this.strRedis.set(key, obj, 7L, TimeUnit.DAYS);
    }

    public void set(String key, Object obj, long timeout) {
        this.strRedis.set(key, obj, timeout, TimeUnit.MILLISECONDS);
    }

    public void setDays(String key, Object obj, long timeout) {
        this.strRedis.set(key, obj, timeout, TimeUnit.DAYS);
    }

    public void set(String key, Object obj, long timeout, TimeUnit unit) {
        this.strRedis.set(key, obj, timeout, unit);
    }

    public void setOpsForHash(String key, HashMap<String, Object> map) {
        this.opsForHash.putAll(key, map);
    }


    public void setNoUpdateTime(String key, Object obj) {
        this.strRedis.set(key, obj, 0L);
    }

    public boolean add(String key, Object obj) {
        return this.strRedis.setIfAbsent(key, obj);
    }

    public Object get(String key) {
        return this.strRedis.get(key);
    }

    public int getInt(String key) {
        Object obj = this.strRedis.get(key);
        if (obj == null)
            return 0;
        if (obj instanceof Integer)
            return (Integer) obj;
        log.error("类型错误 key={}", key);
        return 0;
    }

    public void setJson(String key, Object obj, long timeout, TimeUnit unit) {
        String json = JSON.toJSONString(obj);
        stringRedisTemplate.opsForValue().set(key, json, timeout, unit);
    }

    public <T> T get(String key, Class<T> clazz) {
        String result = getStr(key);
        return JSON.parseObject(result, clazz);
    }

    public <T> T get(String key, TypeReference<T> type) {
        String result = this.stringRedisTemplate.opsForValue().get(key);
        if (result == null)
            return null;
        return JSON.parseObject(result, type);
    }

    public Set<String> keys(String pattern) {
        return this.redisTemplate.keys(pattern);
    }


    public List<Object> multiGet(String key) {
        Set<String> keys = this.redisTemplate.keys(key);
        if (keys == null || keys.isEmpty()) {
            return List.of();
        }
        return this.strRedis.multiGet(keys);
    }

    public String getStr(String key) {
        Object obj = get(key);
        if (obj != null)
            return obj.toString();
        return "";
    }

    public boolean hasKey(String key) {
        try {
            return Boolean.TRUE.equals(this.redisTemplate.hasKey(key));
        } catch (Exception e) {
            log.error("异常信息", e);
            return false;
        }
    }

    public void setAll(Map<String, Object> map, long timeout) {
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            this.strRedis.set(key, value, timeout, TimeUnit.MILLISECONDS);
        }
    }

    public void removeKey(String key) {
        this.strRedis.set(key, "", 1L, TimeUnit.MILLISECONDS);
    }

    public Long getExpire(String key) {
        return this.redisTemplate.getExpire(key);
    }


    public boolean expireMilliseconds(String key, long timeout) {
        return Boolean.TRUE.equals(this.redisTemplate.expire(key, timeout, TimeUnit.MILLISECONDS));
    }

    public boolean expireMicroseconds(String key, long timeout) {
        return Boolean.TRUE.equals(this.redisTemplate.expire(key, timeout, TimeUnit.MICROSECONDS));
    }

    public boolean expireDay(String key, long days) {
        return Boolean.TRUE.equals(this.redisTemplate.expire(key, days, TimeUnit.DAYS));
    }

    public void delete(String key) {
        this.redisTemplate.delete(key);
    }

    public long incr(String key, long delta) {
        if (delta < 0L)
            throw new RuntimeException("Greater than 0");
        return this.redisTemplate.opsForValue().increment(key, delta);
    }

    public void addSet(String key, Set<?> set, long timeout) {
        this.opsForSet.add(key, set);
        this.redisTemplate.expire(key, timeout, TimeUnit.MILLISECONDS);
    }

    public Set<Object> getSetByKey(String key) {
       return this.opsForSet.members(key);
    }

    public Boolean executeLuaScript(String script, List<String> keys, Object... args) {
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Boolean.class);
        return redisTemplate.execute(redisScript, keys, args);
    }

    public Integer executeLuaScriptInteger(String script, List<String> keys, Object... args) {
        DefaultRedisScript<Integer> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Integer.class);
        return redisTemplate.execute(redisScript, keys, args);
    }

    public Long executeLuaScriptLong(String script, List<String> keys, Object... args) {
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);
        return redisTemplate.execute(redisScript, keys, args);
    }
}
